//
//  STDSGraphicEffect.h
//  STDSRenderSDKIOS
//
//  Created by 陈学明 on 2023/4/6.
//

#ifndef STDSGraphicEffect_h
#define STDSGraphicEffect_h
#include <iostream>
#include <fstream>
#include <sstream>
#include "STDSBaseEffect.hpp"
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
#include "STDSHotShaderEffect.hpp"
#include <map>
using namespace rapidjson;
using namespace std;


struct STDSGraphicUnit {
    shared_ptr<STDSHotShaderEffect> effect{nullptr};
    map<string, string> inputMap;
    string name;
    float scale{1.0};
    string path;
};
/// 热加载滤镜
/// 热加载滤镜文件结构
/// root-
///    ｜- paraments.json
///    ｜- filter.json
///    ｜- effect1/
///    ｜- effect2/
class STDSGraphicEffect: public STDSBaseEffect {
public:
    STDSGraphicEffect(string effect, string path) {
        mEffectName = effect;
        mRoot = path;
        mEffectType = STDSEffectTypeGraphic;
        parseEffects();
    }
    
    
    ~STDSGraphicEffect() {
//        printf("dealloc--STDSHotShaderEffect\n");
    }
    
    void initGL() override {
        for (auto &item: mEffects) {
            item->effect->setContext(mContext);
        }
    }
    
    string getEffectName() {
        return mEffectName;
    }
    
    void setFloatArguments(const char *effectName, const char *name, float *floatValue, int n) {
        for (auto &item: mEffects) {
            if (strcmp(item->effect->getEffectName().c_str(), effectName) == 0) {
                item->effect->setFloatArguments(name, floatValue, n);
            }
        }
    }
    
    void setIntArtuments(const char *effectName, const char *name, int *intValue, int n) {
        for (auto &item: mEffects) {
            if (strcmp(item->effect->getEffectName().c_str(), effectName) == 0) {
                item->effect->setIntArtuments(name, intValue, n);
            }
        }
    }
    
    void setStringArtuments(const char *effectName, const char *name, const char *stringValue) {
        for (auto &item: mEffects) {
            if (strcmp(item->effect->getEffectName().c_str(), effectName) == 0) {
                item->effect->setStringArtuments(name, stringValue);
            }
        }
    }
    
    void draw() override {

        map<string, shared_ptr<STDSRenderTarget>> renderBufferMap;
        for (auto item: mEffects) {
            auto size = mSizeSRC * item->scale;
            auto fbo = make_shared<STDSRenderTarget>(size.x, size.y);
            fbo->bindSelf();
            // 设置参数
            item->effect->mSizeSRC = size;
            item->effect->setTexture(mTexture);
            item->effect->setBodyTexture(mBodyTexture);
            for (auto [key, value]: item->inputMap) {
                if (strcmp(value.c_str(), "Src") == 0) {
                    item->effect->setTextureUniform(key.c_str(), mTexture);
                } else if (strcmp(value.c_str(), "Body") == 0) {
                    item->effect->setTextureUniform(key.c_str(), mBodyTexture);
                } else {
                    auto rt = renderBufferMap[value];
                    if (rt != nullptr) {
                        item->effect->setTextureUniform(key.c_str(), rt->getTexture());
                    }
                }
            }
            item->effect->draw();
            fbo->restore();
            
            renderBufferMap.insert(std::pair<string, shared_ptr<STDSRenderTarget>>(item->effect->getEffectName(), fbo));
        }
        auto output = renderBufferMap["output"];
        if (output != nullptr) {
            drawTexture(output->getTexture()->getId());
        }
    }
    
private:
    
    void parseEffects() {
        auto paramentPath = mRoot + "/filter.json";
        ifstream ifs(paramentPath.c_str());
        IStreamWrapper isw(ifs);

        Document d;
        d.ParseStream(isw);
        if (d.IsArray()) {
            for (auto& v : d.GetArray()) {
                
                shared_ptr<STDSGraphicUnit> effectUnit = make_shared<STDSGraphicUnit>();
                if (!v.IsObject()) {
                    continue;
                }
                auto param = v.GetObject();
                auto path = string(param["path"].GetString());
                auto name = string(param["effect"].GetString());
                auto scale = param["scale"].GetFloat();
                effectUnit->path = mRoot + path;
                effectUnit->name = name;
                effectUnit->scale = 1.0;
                if (param["input"].IsArray()) {
                    for (auto& fv : param["input"].GetArray()) {
                        auto fValue = fv.GetObject();
                        auto key = string(fValue["key"].GetString());
                        auto value = string(fValue["value"].GetString());
                        effectUnit->inputMap.insert(pair<string, string>(key, value));
                    }
                }
                effectUnit->effect = make_shared<STDSHotShaderEffect>(name, effectUnit->path);
                mEffects.push_back(effectUnit);
            }
        }
    }
    
private:
    shared_ptr<STDSShaderProgram> mHotProgram{nullptr};
    string mEffectName{""};
    string mRoot{""};
    vector<shared_ptr<STDSGraphicUnit>> mEffects;
};
#endif /* STDSGraphicShader_h */
